package com.hospital.post.service.impl;

import cn.hutool.json.JSONUtil;
import com.hospital.common.base.PageResult;
import com.hospital.common.pojo.TPostContent;
import com.hospital.common.pojo.TPostMeta;
import com.hospital.common.pojo.TReport;
import com.hospital.common.pojo.TSection;
import com.hospital.common.utils.CalculateUtils;
import com.hospital.post.dto.AddFloorDto;
import com.hospital.post.dto.AddPostDto;
import com.hospital.post.dto.AddReportDto;
import com.hospital.post.dto.AttachmentsDto;
import com.hospital.post.exception.ServiceException;
import com.hospital.post.mapper.PostMapper;
import com.hospital.post.service.PostService;
import com.hospital.post.utils.JsonUtils;
import com.hospital.post.utils.RedisUtils;
import com.hospital.post.vo.PostContentVo;
import com.hospital.post.vo.PostMetaVo;
import com.hospital.post.vo.SectionVo;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.ObjectUtils;
import org.springframework.util.StringUtils;

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.TimeUnit;

import static com.hospital.post.exception.enums.ExceptionCode.*;

@Service
public class PostServiceImpl implements PostService
{
    @Autowired
    private PostMapper postMapper;
    @Autowired
    private RedisUtils redisUtils;

    @Override
    public List<TSection> getSections()
    {
        return postMapper.getSections();

    }

    @Override
    public List<SectionVo> getAllSections()
    {
        return postMapper.getAllSections();
    }

    @Override
    public List<PostMetaVo> getPosts(Long sectionId,Integer pinnedOnly, Integer pageNum, Integer pageSize)
    {
        //只看精华
        if(pinnedOnly==1){
            return postMapper.getPosts(sectionId,pinnedOnly, (pageNum-1)*pageSize, pageSize);
        }

        //处理置顶
        List<PostMetaVo> topPosts=postMapper.getTopPosts(sectionId);

        //本页无置顶内容
        if(topPosts.size()<=(pageNum-1)*pageSize){
            return postMapper.getNotTopPosts(sectionId,(pageNum-1)*pageSize-topPosts.size(), pageSize);
        }

        //取本页置顶内容
        int toIndex=Math.min(topPosts.size(), pageNum*pageSize);
        List<PostMetaVo> topPostsTemp=new ArrayList<>(topPosts.subList((pageNum-1)*pageSize,toIndex));

        //取本页非置顶内容
        int notTopPostCount=pageNum*pageSize -toIndex;
        if(notTopPostCount>0){
            topPostsTemp.addAll(postMapper.getNotTopPosts(sectionId, 0, notTopPostCount));
        }

        return topPostsTemp;
    }

    @Override
    public List<PostContentVo> getPostDetail(Long postId, Integer pageNum, Integer pageSize,Integer subPageSize, Long LZOnly)
    {
        List<PostContentVo> topPosts=postMapper.getTopPostDetail(postId, (pageNum-1)*pageSize, pageSize, LZOnly);

        for(PostContentVo post:topPosts){
            Integer subPostCount=postMapper.getSubPostDetailCount(post.getId());
            if(subPostCount>0){
                List<PostContentVo> subPosts= postMapper.getSubPostDetail(post.getId(),0, subPageSize);
                post.setChildren(new PageResult<>((long) subPostCount, subPosts));
            }

        }

        return topPosts;
    }

    @Override
    public Integer getPostCount(Long sectionId,Integer pinnedOnly)
    {
        return postMapper.getPostCount(sectionId, pinnedOnly);
    }

    @Override
    public Integer getPostDetailCount(Long postId,Long LZOnly)
    {
        return postMapper.getPostDetailCount(postId, LZOnly);
    }

    @Override
    @Transactional
    public Long addPost(AddPostDto addPostDto, Long authorId,Integer authorRole)
    {
        //添加帖子元数据
        TPostMeta postMeta=TPostMeta.builder()
                .sectionId(addPostDto.getSectionId())
                .authorId(authorId)
                .authorRole(authorRole)
                .title(addPostDto.getTitle())
                .abstracts(addPostDto.getText().substring(0,Math.min(addPostDto.getText().length(), 20)))
                .totalFloor(1)
                .build();
        if(postMapper.addPostMeta(postMeta)==0){
            throw new ServiceException(INSERT_RECORD_FAILED);
        }

        //添加帖子内容
        TPostContent postContent=TPostContent.builder()
                .metaId(postMeta.getId())
                .opId(authorId)
                .pid(0L)
                .floor(0)
                .html(addPostDto.getHtml())
                .attachments(addPostDto.getAttachments())
                .text(addPostDto.getText())
                .build();
        if(postMapper.addPostContent(postContent)==0){
            throw new ServiceException(INSERT_RECORD_FAILED);
        }

        //添加附件信息到资源表
       if(StringUtils.hasLength(addPostDto.getAttachments()) && !addPostDto.getAttachments().equals("[]"))
       {
           List<AttachmentsDto> attachments = JSONUtil.toList(addPostDto.getAttachments(),AttachmentsDto.class);
           List<String> paths= attachments.stream().map(AttachmentsDto::getFilePath).toList();

           if (postMapper.addResource(authorId, postMeta.getId(), postContent.getId(), JsonUtils.toJson(paths)) == 0)
           {
               //throw new ServiceException(INSERT_RECORD_FAILED);
               System.out.println("添加附件信息到资源表失败");
           }
       }
        return postMeta.getId();
    }

    @Override
    public PostContentVo getFloor(Long floorId, Integer subPageSize)
    {
        List<PostContentVo> floors=postMapper.getFloor(floorId, subPageSize);
        if (floors.size()==0){
            throw new ServiceException(RECORD_NOT_FOUND);
        }
        PostContentVo floor=floors.get(0);
        Integer subPostCount=postMapper.getSubPostDetailCount(floorId);
        if(subPostCount>0){
            List<PostContentVo> subPosts= postMapper.getSubPostDetail(floorId,0, subPageSize);
            floor.setChildren(new PageResult<>((long) subPostCount, subPosts));
        }
        return floor;
    }

    @Override
    @Transactional
    public Long addFloor(AddFloorDto addFloorDto, Long authorId, Integer authorRole)
    {
        //查询元数据
        List<TPostMeta> postMetas=postMapper.getPostMetaById(addFloorDto.getMetaId());
        if (postMetas.size()==0){
            throw new ServiceException(RECORD_NOT_FOUND, "帖子不存在");
        }
        TPostMeta postMeta=postMetas.get(0);

        //添加帖子内容
        TPostContent postContent=TPostContent.builder()
                .metaId(addFloorDto.getMetaId())
                .opId(authorId)
                .pid(addFloorDto.getRepliedId())
                .floor(addFloorDto.getRepliedId()==0L?postMeta.getTotalFloor():-1)
                .html(addFloorDto.getHtml())
                .attachments(addFloorDto.getAttachments())
                .text(addFloorDto.getText())
                .build();
        if(postMapper.addPostContent(postContent)==0){
            throw new ServiceException(INSERT_RECORD_FAILED);
        }

        //更新元数据
        postMeta.setReplyCount(postMeta.getReplyCount()+1);
        if(addFloorDto.getRepliedId()==0L){
            postMeta.setTotalFloor(postMeta.getTotalFloor()+1);
        }
        postMeta.setHeatScore(CalculateUtils.calculateHeatScore(postMeta.getLikeCount(), postMeta.getReplyCount(), postMeta.getFavoriteCount()));
        if(postMapper.updatePostMeta(postMeta)==0){
            throw new ServiceException(UPDATE_RECORD_FAILED);
        }

        //添加附件信息到资源表
        if(StringUtils.hasLength(addFloorDto.getAttachments()) && !addFloorDto.getAttachments().equals("[]"))
        {
            List<AttachmentsDto> attachments = JSONUtil.toList(addFloorDto.getAttachments(), AttachmentsDto.class);
            List<String> paths = attachments.stream().map(AttachmentsDto::getFilePath).toList();
            if (postMapper.addResource(authorId, postMeta.getId(), postContent.getId(),JsonUtils.toJson(paths)) == 0)
            {
                //throw new ServiceException(INSERT_RECORD_FAILED);
                System.out.println("添加附件信息到资源表失败");
            }
        }
        return postContent.getId();
    }

    @Override
    public Boolean getHasLiked(Long userId, Long metaId)
    {
        return redisUtils.hasKey("post:like:"+userId+":"+metaId);
    }

    @Override
    public Boolean getHasBeenFavorite(Long userId, Long metaId)
    {
        return redisUtils.hasKey("post:favorite:"+userId+":"+metaId);
    }

    @Override
    public Boolean addLike(Long userId, Long metaId,Integer like)
    {
        if(like==1 && !redisUtils.hasKey("post:like:"+userId+":"+metaId)){
            redisUtils.set("post:like:"+userId+":"+metaId, 1,24, TimeUnit.HOURS);
            redisUtils.hIncr("post:like:stat",String.valueOf(metaId),1);

        }

        if(like==0 && redisUtils.hasKey("post:like:"+userId+":"+metaId)){
            redisUtils.del("post:like:"+userId+":"+metaId);
            redisUtils.hIncr("post:like:stat",String.valueOf(metaId),-1);
        }

        return true;
    }

    @Override
    public Boolean addFavorite(Long userId, Long metaId, Integer favorite)
    {
        if(favorite==1 && !redisUtils.hasKey("post:favorite:"+userId+":"+metaId)){
            redisUtils.set("post:favorite:"+userId+":"+metaId, 1);
            redisUtils.hIncr("post:favorite:stat",String.valueOf(metaId),1);
            //redisUtils.sAdd("post:favorite:set:"+userId, metaId);
            if(postMapper.selectFavorate(userId, metaId)==0){
                postMapper.addFavorate(userId, metaId);
            }
            postMapper.updateFavoratePlus(metaId);
        }

        if(favorite==0 && redisUtils.hasKey("post:favorite:"+userId+":"+metaId)){
            redisUtils.del("post:favorite:"+userId+":"+metaId);
            redisUtils.hIncr("post:favorite:stat",String.valueOf(metaId),-1);
            //redisUtils.sRem("post:favorite:set:"+userId, metaId);
            postMapper.deleteFavorate(userId, metaId);
            postMapper.updateFavorateMinus(metaId);
        }

        return true;
    }

    @Override
    public Integer getFavoritesCount(Long userId)
    {
        return postMapper.getFavoritesCount(userId);
    }

    @Override
    public List<PostMetaVo> getFavorites(Long userId, Integer pageNum, Integer pageSize) {
        return postMapper.getFavorites(userId,(pageNum-1)*pageSize,pageSize);
    }

    @Override
    public Boolean redisLikeToMysql()
    {
        redisUtils.hGetAll("post:like:stat").forEach((key, value) -> postMapper.updatePostMetaLike(Long.valueOf(key.toString()), (Integer) value));

        return true;
    }

    @Override
    public Long getAuthorIdByMetaId(Long metaId)
    {
        List<Long> authorIds= postMapper.getAuthorIdByMetaId(metaId);
        return ObjectUtils.isEmpty(authorIds) ?null:authorIds.get(0);
    }

    @Override
    @Transactional
    public Integer deletePost(Long metaId,Long userId)
    {
        Integer delMeta= postMapper.deletePost(metaId, userId);
        Integer delContent= postMapper.deleteFloors(metaId, userId);
        if(delMeta==0||delContent==0){
            throw new ServiceException(DELETE_RECORD_FAILED);
        }
        return delMeta*delContent;
    }

    @Override
    public Long getAuthorIdByFloorId(Long floorId)
    {
        List<Long> authorIds= postMapper.getAuthorIdByFloorId(floorId);
        return ObjectUtils.isEmpty(authorIds)?null:authorIds.get(0);
    }

    @Override
    public Integer deleteFloor(Long floorId, Long userId)
    {
        return postMapper.deleteFloor(floorId, userId);
    }

    @Override
    public Boolean addReport(AddReportDto addReportDto, Long userId)
    {
        TReport report=TReport.builder()
                .metaId(addReportDto.getMetaId())
                .reportType(addReportDto.getReportType())
                .reportReason(addReportDto.getReportReason())
                .postContentId(addReportDto.getPostContentId())
                .reportTargetId(addReportDto.getReportTargetId())
                .reportSupportId(userId)
                .reportStatus(0)
                .build();
        return postMapper.addReport(report)>0;
    }

    @Override
    public Long getSectionIdByMetaId(Long metaId)
    {
        List<Long> sectionIds= postMapper.getSectionIdByMetaId(metaId);
        System.out.println(sectionIds.toString());
        return ObjectUtils.isEmpty(sectionIds)?null:sectionIds.get(0);
    }

    @Override
    public Integer getMyPostsCount(Long userId) {
        return postMapper.getMyPostsCount(userId);
    }

    @Override
    public List<PostMetaVo> getMyPosts(Long userId, Integer pageNum, Integer pageSize) {
        List<PostMetaVo> posts=postMapper.getMyPosts(userId,(pageNum-1)*pageSize,pageSize);
        return ObjectUtils.isEmpty(posts)?null: posts;
    }

    @Override
    public Integer getMyRepliesCount(Long userId) {
        return postMapper.getMyRepliesCount(userId);
    }

    @Override
    public List<PostContentVo> getMyReplies(Long userId, Integer pageNum, Integer pageSize) {
        List<PostContentVo> replies=postMapper.getMyReplies(userId,(pageNum-1)*pageSize,pageSize);
        for(PostContentVo post:replies){
            List<String> metaTitle=postMapper.getPostTitleByMetaId(post.getMetaId());
            post.setHtml(ObjectUtils.isEmpty(metaTitle)?null:metaTitle.get(0));
            List<Long> postAuthor=postMapper.getAuthorIdByMetaId(post.getMetaId());
            post.setOpId(ObjectUtils.isEmpty(postAuthor)?null:postAuthor.get(0));
        }
        return ObjectUtils.isEmpty(replies)?null: replies;
    }

    @Override
    public Integer getSubPostCount(Long floorId) {
        return postMapper.getSubPostDetailCount(floorId);
    }

    @Override
    public List<PostContentVo> getSubPosts(Long floorId, Integer pageNum, Integer pageSize) {
        return postMapper.getSubPostDetail(floorId,(pageNum-1)*pageSize,pageSize);
    }


}
